/*
 * Copyright (C) 2011, Pavel Samko <bulldozerbsg@gmail.com>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the "Pavel Samko" nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

#include "VCTCPClient.h"

VCTCPClientThread::VCTCPClientThread(VCTCPClient & mod)
: VCThread()
, m_mod(mod)
{

}

int VCTCPClientThread::Execute(void * data)
{
	while (!m_mod.m_needexit)
	{
		// Send
		{
			VCAutoMutex m_bufflock(m_mod.m_send_buff_mutex);
			size_t data_size = m_mod.m_send_buff.Size();
			if (data_size)
			{
				VCAutoMutex m_socklock(m_mod.m_socket_mutex);
				size_t send_size = data_size;
				if (m_mod.m_socket != VC_SOCKET_INVALID)
				{
					if (VCNetSend(m_mod.m_socket, m_mod.m_send_buff.GetDataPtr(), &send_size))
					{
						m_mod.m_send_buff.PopSize(send_size);
					} else
					{
						TRACE("tcp client send false")
						VCNetClose(m_mod.m_socket);
						m_mod.m_socket = VC_SOCKET_INVALID;
						m_mod.OnDisconect();
					}
				}
			}
		}
		// Recv
		{
			VCAutoMutex m_socklock(m_mod.m_socket_mutex);
			size_t recv_size = VCTCPCLIENT_BUFF_SIZE;
			if (m_mod.m_socket != VC_SOCKET_INVALID)
			{
				if (VCNetRecv(m_mod.m_socket, m_recv_buff, &recv_size))
				{
					m_mod.OnRecv(m_recv_buff, recv_size);
				} else
				{
					TRACE("tcp client recv false")
					VCNetClose(m_mod.m_socket);
					m_mod.m_socket = VC_SOCKET_INVALID;
					m_mod.OnDisconect();
				}
			}
		}
		VCSleep(1);
	}
	return 0;
}

void VCTCPClientThread::OnExit(int exitcode)
{

}

VCTCPClient::VCTCPClient()
: m_needexit(false)
, m_socket(VC_SOCKET_INVALID)
, m_send_buff(VCTCPCLIENT_BUFF_SIZE)
{
	VCNetInit();
	m_thread = new VCTCPClientThread(*this);
}

VCTCPClient::~VCTCPClient()
{
	Disconnect(true);
	m_needexit = true;
	if (m_thread)
	{
		m_thread->Wait();
		delete m_thread;
	}
	VCNetDeinit();
}

bool VCTCPClient::Connect(const char * to_ip, unsigned short to_port)
{
	bool result = false;
	Disconnect(false);
	{
		VCAutoMutex lock(m_socket_mutex);
		VC_SOCKET_ADDR_IN to;
		if (VCNetInitAddrIn(&to, to_ip, to_port))
		{
			m_socket = VCNetSocketTCP();
			if (m_socket != VC_SOCKET_INVALID)
			{
				if (VCNetConnect(m_socket, &to))
				{
					result = true;
				}
				if (!result)
				{
					TRACE("tcp client connect false")
					VCNetClose(m_socket);
					m_socket = VC_SOCKET_INVALID;
				}
			}
		}
	}
	return result;
}

void VCTCPClient::Disconnect(bool wait_send_all)
{
	//if (wait_send_all)
	//{
	//	size_t buff_size = 0;
	//	do
	//	{
	//		if (buff_size)
	//		{
	//			VCSleep(1);
	//		}
	//		{
	//			VCAutoMutex lock(m_send_buff_mutex);
	//			buff_size = m_send_buff.Size();
	//		}
	//	} while (buff_size)
	//} else
	{
		ResetSendBuff();
	}
	{
		VCAutoMutex lock(m_socket_mutex);
		if (m_socket != VC_SOCKET_INVALID)
		{
			VCNetClose(m_socket);
			m_socket = VC_SOCKET_INVALID;
			OnDisconect();
		}
	}
}

bool VCTCPClient::IsConnected()
{
	VCAutoMutex lock(m_socket_mutex);
	return (m_socket != VC_SOCKET_INVALID);
}

void VCTCPClient::Send(void * data, size_t data_size)
{
	if (data && data_size)
	{
		VCAutoMutex lock(m_send_buff_mutex);
		m_send_buff.Push(data, data_size);
	}
}

void VCTCPClient::OnRecv(void * data, size_t data_size)
{
	// need override
}

void VCTCPClient::OnDisconect()
{
	// need override
}

void VCTCPClient::ResetSendBuff()
{
	VCAutoMutex lock(m_send_buff_mutex);
	size_t buff_size = m_send_buff.Size();
	m_send_buff.PopSize(buff_size);
}

